/**
 * Copyright 2011, Felix Palmer
 *
 * Licensed under the MIT license:
 * http://creativecommons.org/licenses/MIT/
 */
package com.andatsoft.laisim.visualizer;

import java.util.HashSet;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.media.audiofx.Visualizer;
import android.util.AttributeSet;
import android.view.View;

import com.andatsoft.laisim.service.PlayerService;
import com.andatsoft.laisim.visualizer.renderer.Renderer;

/**
 * A class that draws visualizations of data received from a
 * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture } and
 * {@link Visualizer.OnDataCaptureListener#onFftDataCapture }
 */
public class VisualizerView extends View {
	// private static final String TAG = "VisualizerView";

	private byte[] mBytes;
	private byte[] mFFTBytes;
	private AudioData mAudioData;
	private FFTData mFFTData;
	private Rect mRect = new Rect();
	private Visualizer mVisualizer;
	private Matrix mMatrix;

	private Set<Renderer> mRenderers;

	private Paint mFlashPaint = new Paint();
	private Paint mFadePaint = new Paint();

	public VisualizerView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs);
		init();
	}

	public VisualizerView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public VisualizerView(Context context, int songId) {
		this(context, null, 0);
	}

	private void init() {
		mBytes = null;
		mFFTBytes = null;
		mAudioData = new AudioData(mBytes);
		mFFTData = new FFTData(mFFTBytes);
		mMatrix = new Matrix();

		mFlashPaint.setColor(Color.argb(122, 255, 255, 255));
		mFadePaint.setColor(Color.argb(230, 255, 255, 255)); // Adjust alpha to
																// change how
																// quickly the
																// image fades
		mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));

		mRenderers = new HashSet<Renderer>();
	}

	/**
	 * Links the visualizer to a player
	 * 
	 * @param player
	 *            - MediaPlayer instance to link to
	 */
	public boolean link(MediaPlayer player) {
		if (player == null) {
			throw new NullPointerException("Cannot link to null MediaPlayer");
		}

		// Create the Visualizer object and attach it to our media player.
		try {
			mVisualizer = new Visualizer(player.getAudioSessionId());
		} catch (RuntimeException re) {
			return false;
		}
		mVisualizer.setEnabled(false);
		mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);

		// Pass through Visualizer data to VisualizerView
		Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener() {
			@Override
			public void onWaveFormDataCapture(Visualizer visualizer,
					byte[] bytes, int samplingRate) {
				updateVisualizer(bytes);
			}

			@Override
			public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
					int samplingRate) {
				updateVisualizerFFT(bytes);
			}
		};

		mVisualizer.setDataCaptureListener(captureListener,
				Visualizer.getMaxCaptureRate() / 2, true, true);

		// Enabled Visualizer and disable when we're done with the stream
		mVisualizer.setEnabled(true);
		// player.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
		// {
		// @Override
		// public void onCompletion(MediaPlayer mediaPlayer)
		// {
		// mVisualizer.setEnabled(false);
		// }
		// });
		return true;
	}

	public void addUniqueRenderer(Renderer renderer) {
		clearRenderers();
		addRenderer(renderer);
	}

	public void addRenderer(Renderer renderer) {
		if (renderer != null) {
			mRenderers.add(renderer);
		}
	}

	public void clearRenderers() {
		mRenderers.clear();
	}

	/**
	 * Call to release the resources used by VisualizerView. Like with the
	 * MediaPlayer it is good practice to call this method
	 */
	public void release() {
		mVisualizer.release();
	}

	/**
	 * Pass data to the visualizer. Typically this will be obtained from the
	 * Android Visualizer.OnDataCaptureListener call back. See
	 * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
	 * 
	 * @param bytes
	 */
	public void updateVisualizer(byte[] bytes) {
		mBytes = bytes;
		invalidate();
	}

	/**
	 * Pass FFT data to the visualizer. Typically this will be obtained from the
	 * Android Visualizer.OnDataCaptureListener call back. See
	 * {@link Visualizer.OnDataCaptureListener#onFftDataCapture }
	 * 
	 * @param bytes
	 */
	public void updateVisualizerFFT(byte[] bytes) {
		mFFTBytes = bytes;
		invalidate();
	}

	boolean mFlash = true;

	/**
	 * Call this to make the visualizer flash. Useful for flashing at the start
	 * of a song/loop etc...
	 */
	public void flash() {
		mFlash = true;
		invalidate();
	}

	Bitmap mCanvasBitmap;
	Canvas mCanvas;
	private float scale = 1.5f;
	// 0: scale
	// 1: translate
	// 2: skew
	private int mEffectMode = 0;
	private boolean mIsZoomIn = true;
	private boolean mIsTransLeft = true;
	private PointF mTranslatePoint = new PointF();
	private float mSkew = 0f;
	private boolean mIsSkewA = true;

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// Create canvas once we're ready to draw
		mRect.set(0, 0, getWidth(), getHeight());

		if (mCanvasBitmap == null) {
			mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(),
					canvas.getHeight(), Config.ARGB_8888);
		}
		if (mCanvas == null) {
			mCanvas = new Canvas(mCanvasBitmap);
		}
		if (PlayerService.isMediaCompelete) {
			// Fade out old contents
			mCanvas.drawPaint(mFadePaint);
			canvas.drawBitmap(mCanvasBitmap, mMatrix, null);
			return;
		}

		if (mBytes != null) {
			// Render all audio renderers
			mAudioData.bytes = mBytes;
			for (Renderer r : mRenderers) {
				r.render(mCanvas, mAudioData, mRect);
			}
		}

		if (mFFTBytes != null) {
			// Render all FFT renderers
			mFFTData.bytes = mFFTBytes;
			for (Renderer r : mRenderers) {
				r.render(mCanvas, mFFTData, mRect);
			}
		}

		// Fade out old contents
		mCanvas.drawPaint(mFadePaint);

		if (mFlash) {
			mFlash = false;
			mCanvas.drawPaint(mFlashPaint);
		}
		if (mEffectMode == 0) {
			if (mIsZoomIn) {
				scale += 0.01f;
				if (scale > 2.5f) {
					scale -= 0.01f;
					mIsZoomIn = false;
					mEffectMode = 1;
				}
			} else {
				scale -= 0.01f;
				if (scale <= 1.5f) {
					scale -= 0.01f;
					mIsZoomIn = true;
				}
			}
			mMatrix.setScale(scale, scale, mRect.width() / 2,
					mRect.height() / 2);
		} else if (mEffectMode == 1) {

			if (mIsTransLeft) {
				mTranslatePoint.x += 1.5f;
				if (mTranslatePoint.x > 100f) {
					mIsTransLeft = false;
				}
			} else {
				mTranslatePoint.x -= 1.5f;
				if (mTranslatePoint.x < 0f) {
					mIsTransLeft = true;
					mEffectMode = 2;
				}

			}

			mMatrix.setScale(scale, scale, mRect.width() / 2,
					mRect.height() / 2);
			mMatrix.postTranslate(mTranslatePoint.x, mTranslatePoint.y);
		} else if (mEffectMode == 2) {
			
			if (mIsSkewA) {
				mSkew += 0.001f;
				if (mSkew > 0.2f) {
					mIsSkewA = false;
				}
			} else {
				mSkew -= 0.001f;
				if (mSkew < -0.1f) {
					mIsSkewA = true;
					mEffectMode = 0;
				}

			}
			mMatrix.setScale(scale, scale, mRect.width() / 2,
					mRect.height() / 2);
			mMatrix.postSkew(mSkew, mSkew, mRect.width() / 2,
					mRect.height() / 2);
		}

		canvas.drawBitmap(mCanvasBitmap, mMatrix, null);
	}
}